﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace Coronox.SkyShell
{
    public enum StopSyncOption
    {
        Detach,
        Delete,
        UserChoice
    }

    public static class Sky
    {
        public static string GetSkyDriveFolder()
        {
            return SkyDrive.GetSkyDriveUserFolder();
        }

        public static void SyncFolder(string folderPath, string alternativeName = null, bool mergeFolder = false)
        {
            //  System.Windows.Forms.MessageBox.Show(string.Format("{0} {1} {2}",folderPath, alternativeName, mergeFolder));
            string reason;
            if (!CanSyncFolder(folderPath, out reason, true))
            {
                System.Windows.Forms.MessageBox.Show(reason, "ERROR", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);

                return;
            }
            string targetPath;
            if (alternativeName == null)
                targetPath = SkyDrive.DetermineTargetPath(folderPath);
            else
                targetPath = Path.Combine(SkyDrive.GetSkyDriveUserFolder(), alternativeName);

            if (!mergeFolder && Directory.Exists(targetPath))
            {
                // System.Windows.Forms.MessageBox.Show("1");
                //invoke UI
                var loc = System.Reflection.Assembly.GetExecutingAssembly().Location;
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    //loc = @"..\..\..\..\SkyShell\bin\Debug\SkyShell.exe";
                    FileInfo fi = new FileInfo(loc);
                    loc = Path.Combine(fi.DirectoryName, "SkyShell.exe");
                }
                else
                {
                    FileInfo fi = new FileInfo(loc);
                    loc = Path.Combine(fi.DirectoryName, "SkyShell.exe");
                }
                Process proc = new Process();
                ProcessStartInfo pif = new ProcessStartInfo();
                pif.FileName = loc;

                DirectoryInfo di = new DirectoryInfo(targetPath);
                string existingJunction = SkyDrive.GetRegSkyShellJunction(di.Name);
                if (string.IsNullOrEmpty(existingJunction))
                    pif.Arguments = "/sync " + string.Format("\"{0}\"", folderPath) + " " + string.Format("\"{0}\"", targetPath);
                else
                    pif.Arguments = "/localsync " + string.Format("\"{0}\"", folderPath) + " " + string.Format("\"{0}\"", targetPath);
                proc.StartInfo = pif;
                //  System.Windows.Forms.MessageBox.Show(pif.FileName + " " + pif.Arguments);
                proc.Start();
            }
            else
            {
                //    System.Windows.Forms.MessageBox.Show("2");
                var sourceDrive = FolderOperator.GetFolderDriveInfo(folderPath);
                var targetDrive = FolderOperator.GetFolderDriveInfo(targetPath);

                bool showUI = sourceDrive.Name != targetDrive.Name || mergeFolder;
                if (FolderOperator.Move(folderPath, targetPath, showUI))
                {
                    FolderOperator.CreateJunction(folderPath, targetPath, true);
                    FolderOperator.CreateFolderIcon(targetPath);
                    FolderOperator.CreateFolderIcon(folderPath);

                    DirectoryInfo sourceDir = new DirectoryInfo(folderPath);
                    if (string.IsNullOrEmpty(alternativeName))
                        SkyDrive.SetRegSkyShellJunction(sourceDir.Name, folderPath);
                    else
                        SkyDrive.SetRegSkyShellJunction(alternativeName, folderPath);
                }
            }
        }

        public static void StopSyncFolder(string folderPath, StopSyncOption option)
        {

            string reason;
            if (!CanStopSyncFolder(folderPath, out reason))
                throw new ArgumentException(reason);

            string folderNameWitninSkydrive;
            DirectoryInfo junctionFolder;
            if (folderPath.ToLower().StartsWith(SkyDrive.GetSkyDriveUserFolder().ToLower()))
            {
                //ok we look at SkyDrive folder, lets get the Junction form the registry
                DirectoryInfo di = new DirectoryInfo(folderPath);
                junctionFolder = new DirectoryInfo(SkyDrive.GetRegSkyShellJunction(di.Name));
                folderNameWitninSkydrive = di.Name;

            }
            else
            {

                junctionFolder = new DirectoryInfo(folderPath);
                folderNameWitninSkydrive = junctionFolder.Name;
            }


            if (FolderOperator.IsJunction(junctionFolder.FullName))
            {
                //ok, it's a junction, let's see whether the target is in the SkyDrive folder.
                //string targetPath = FolderOperator.GetReparsePoint(folderPath);
                string targetPath = FolderOperator.GetReparsePoint(junctionFolder.FullName);
                string skydriveFoder = SkyDrive.GetSkyDriveUserFolder();
                if (targetPath.ToLower().StartsWith(skydriveFoder.ToLower()))
                {
                    switch (option)
                    {
                        case StopSyncOption.Detach:
                            {
                                FolderOperator.RemoveFolderIcon(junctionFolder.FullName);
                                if (FolderOperator.DeleteJunction(junctionFolder.FullName))
                                {
                                    if (FolderOperator.Copy(targetPath, junctionFolder.FullName))
                                    {
                                        SkyDrive.RemoveRegSkyShellJunction(junctionFolder.Name);
                                    }
                                }
                                break;
                            }
                        case StopSyncOption.Delete:
                            {
                                FolderOperator.RemoveFolderIcon(junctionFolder.FullName);
                                if (FolderOperator.DeleteJunction(junctionFolder.FullName))
                                {
                                    var sourceDrive = FolderOperator.GetFolderDriveInfo(junctionFolder.FullName);
                                    var targetDrive = FolderOperator.GetFolderDriveInfo(targetPath);

                                    bool showUI = sourceDrive.Name != targetDrive.Name;
                                    if (FolderOperator.Move(targetPath, junctionFolder.FullName, showUI))
                                    {
                                        SkyDrive.RemoveRegSkyShellJunction(junctionFolder.Name);
                                    }
                                }
                            }
                            break;
                        case StopSyncOption.UserChoice:
                            {
                                //invoke UI
                                //invoke UI
                                var loc = System.Reflection.Assembly.GetExecutingAssembly().Location;
                                if (System.Diagnostics.Debugger.IsAttached)
                                {
                                    //loc = @"..\..\..\..\SkyShell\bin\Debug\SkyShell.exe";
                                    FileInfo fi = new FileInfo(loc);
                                    loc = Path.Combine(fi.DirectoryName, "SkyShell.exe");
                                }
                                else
                                {
                                    FileInfo fi = new FileInfo(loc);
                                    loc = Path.Combine(fi.DirectoryName, "SkyShell.exe");
                                }
                                Process proc = new Process();
                                ProcessStartInfo pif = new ProcessStartInfo();
                                pif.FileName = loc;
                                pif.Arguments = "/stop " + string.Format("\"{0}\"", junctionFolder.FullName);
                                proc.StartInfo = pif;
                                proc.Start();
                                break;
                            }
                        default:
                            break;
                    }
                }
            }

        }

        /// <summary>
        /// Shares a folder.
        /// </summary>
        /// <param name="folderPath">The path to the folder.</param>
        public static void ShareFolder(string folderPath)
        {
            DirectoryInfo di = new DirectoryInfo(folderPath);
            var loc = System.Reflection.Assembly.GetExecutingAssembly().Location;
            if (System.Diagnostics.Debugger.IsAttached)
            {
                loc = @"..\..\SkyShell\bin\Debug\SkyShell.exe";
            }
            else
            {
                FileInfo fi = new FileInfo(loc);
                loc = Path.Combine(fi.DirectoryName, "SkyShell.exe");
            }
            Process proc = new Process();
            ProcessStartInfo pif = new ProcessStartInfo();
            pif.FileName = loc;
            pif.Arguments = "/sharecontent " + string.Format("\"{0}\"", di.FullName);
            proc.StartInfo = pif;
            proc.Start();

        }

        /// <summary>
        /// Checks whether a folder can be synced.
        /// </summary>
        /// <param name="folderPath">The path to the folder.</param>
        /// <param name="reason">An out parameter that gives a reason in case the folder can't be synced.</param>
        /// <returns>A value that indicates whether the folder can be synced or not.</returns>
        public static bool CanSyncFolder(string folderPath, out string reason, bool deepCheck)
        {
            reason = null;
            if (SkyDrive.IsInstalled() == false)
            {
                reason = "SkyDrive is not installed on this computer.";
                return false;
            }
            if (folderPath.Trim().ToLower().StartsWith(SkyDrive.GetSkyDriveUserFolder().ToLower()) == true)
            {
                reason = "Folder is already in SkyDrive.";
                return false;
            }
            if (SkyDrive.GetSkyDriveUserFolder().ToLower().Contains(folderPath.Trim().ToLower()) == true)
            {
                reason = "Folder is parent of SkyDrive.";
                return false;
            }

            if (FolderOperator.IsJunction(folderPath))
            {
                reason = "This path is already a reparse point and can't be synced.";
                return false;
            }



            var drive = FolderOperator.GetFolderDriveInfo(folderPath);
            if (drive == null)
            {
                reason = "Folder is not located on a local hard drive.";
                return false;
            }
            if (drive.DriveType != DriveType.Fixed)
            {
                reason = "Folder is located on a non-fixed drive.";
                return false;
            }
            if (drive.DriveFormat != "NTFS")
            {
                reason = "Unsupported file system.";
                return false;
            }

            if (FolderOperator.IsPartOfSystemFolder(folderPath))
            {
                reason = "The folder is part of a system folder that isn't supported.";
                return false;
            }


            if (deepCheck) //possible long running checks
            {
                if (FolderOperator.IsMemberOrContainsJunctionSkyDrive(folderPath))
                {
                    reason = "This path already contains a reparse point and can't be synced.";
                    return false;
                }
            }

            return true;

        }

        /// <summary>
        /// Checks whether the target folder can be merged.
        /// </summary>
        /// <param name="folderPath">The path to the folder.</param>
        /// <param name="reason">An out parameter that gives a reason in case the folder can't be merged.</param>
        /// <returns>A value that indicates whether the folder can be merged or not.</returns>
        public static bool CanMergeFolder(string folderPath, out string reason)
        {
            reason = null;
            if (FolderOperator.IsJunction(folderPath))
            {
                reason = "The target is a reparse point and can't be merged.";
                return false;
            }
            else
            {
                return true;
            }

        }

        /// <summary>
        /// Checks whether the sync relationship for the folder can be stoped.
        /// </summary>
        /// <param name="folderPath">The path to the folder.</param>
        /// <param name="reason">An out parameter that gives a reason in case the folder can't be shared.</param>
        /// <returns>A value that indicates whether the sync relationship can be stopped or not.</returns>
        public static bool CanStopSyncFolder(string folderPath, out string reason)
        {
            reason = null;

            string junctionPath;
            if (folderPath.ToLower().StartsWith(SkyDrive.GetSkyDriveUserFolder().ToLower()))
            {
                //ok we look at SkyDrive folder, lets get the Junction form the registry
                DirectoryInfo di = new DirectoryInfo(folderPath);
                junctionPath = SkyDrive.GetRegSkyShellJunction(di.Name);
                if (string.IsNullOrEmpty(junctionPath))
                {
                    reason = "SkyDrive folder is not known as an reparse point.";
                    return false;
                }
            }
            else
            {
                junctionPath = folderPath;
            }


            if (FolderOperator.IsJunction(junctionPath))
            {
                string targetPath = FolderOperator.GetReparsePoint(junctionPath);
                if (targetPath.ToLower().StartsWith(SkyDrive.GetSkyDriveUserFolder().ToLower()))
                {
                    return true;
                }
                else
                {
                    reason = "This target of the reparse point is not in SkyDrive.";
                    return false;
                }
            }
            else
            {
                reason = "This path is not a reparse point.";
                return false;
            }

        }

        /// <summary>
        /// Checks whether a folder can be shared.
        /// </summary>
        /// <param name="folderPath">The path to the folder.</param>
        /// <param name="reason">An out parameter that gives a reason in case the folder can't be shared.</param>
        /// <returns>A value that indicates whether the folder can be shared or not.</returns>
        public static bool CanShareFolder(string folderPath, out string reason)
        {
            reason = null;

            if (folderPath.ToLower().StartsWith(SkyDrive.GetSkyDriveUserFolder().ToLower()))
            {
                return true;
            }
            else
            {
                reason = "This target is not a SkyDrive folder.";
                return false;
            }
        }

        /// <summary>
        /// Based on a folder path new name will be suggested
        /// </summary>
        /// <param name="folderPath">The path to the folder</param>
        /// <param name="local">A parameter that indicates whether it's a local folder or folder that came via sync over SkyDrive.</param>
        /// <returns>The suggested name for the folder.</returns>
        public static string SuggestAlternativeName(string folderPath, bool local)
        {

            if (Directory.Exists(folderPath))
            {
                DirectoryInfo di = new DirectoryInfo(folderPath);
                DirectoryInfo current = di;
                string folderName = di.Name;
                string newFolderName = null;
                bool newNameFound = false;

                if (local)
                {
                    //it's a local name conflict, so lets walkup the path to solve the conflict
                    //   int suffixCounter = 1;
                    while (!newNameFound)
                    {
                        newFolderName = current.Parent.Name + "-" + current.Name;
                        foreach (var item in Path.GetInvalidFileNameChars())
                        {
                            newFolderName = newFolderName.Replace(item.ToString(), "");
                        }

                        string newPath = Path.Combine(SkyDrive.GetSkyDriveUserFolder(), newFolderName);
                        newNameFound = !Directory.Exists(newPath);

                        current = current.Parent;

                        if (current.Parent == null)
                        {
                            //emergency exit
                            newNameFound = true;

                        }
                    }
                    return newFolderName;
                }
                else
                {
                    //the folder in SkyDrive comes from another machine, so lets put in a machine suffix
                    newFolderName = folderName + "-" + Environment.MachineName;
                    return newFolderName;
                }
            }
            else
            {
                throw new ArgumentException("Folder doesn't exists");

            }
        }


    }
}
